#include <boost/unordered_map.hpp>

#include <stdlib.h>
#include <stdbool.h>
#include "page_management.h"

#include "dfruntime_private.h"

#include "memory_management.h"



/* This file describes how pages are tracked inside a node */
/* The page management is split into two :
  - Acquired(owned) pages.
  - Wished paged.

  The aim of this page tracking is to :
   - Avoid double acquire...
   - Keep track of arrivals of frames.
   - Schedule pages with all pages ready immediately.
   - Allow executor to silently replace output frames with dummy buffers.


*/


typedef void* PtrType;
typedef boost::unordered_map<PtrType,pagedir_elt> PageMap;

static PageMap *page_dir = new PageMap();


/* Old, deprecated version */
/*
bool page_is_acquired( void * page_ptr ) {
    PageMap::iterator it =  page_dir->find(page_ptr);
    return it != page_dir->end() && it->second.acquired;

}
*/

bool page_is_wanted( void * page_ptr ) {
    PageMap::iterator it =  page_dir->find(page_ptr);
    return it != page_dir->end() && !it->second.acquired;

}

bool page_get_direlt( void * page_ptr, struct pagedir_elt * dir) {
    PageMap::iterator it =  page_dir->find(page_ptr);
    if ( it == page_dir->end() ) {
        return false;
    }
    *dir = it->second;

    return true;
}


void page_incr_count( void * page_ptr, size_t size, bool exclusive, bool acquired ) {
    PageMap::iterator ret;
    ret = page_dir->find( page_ptr );

    if ( ret == page_dir->end() ) {
        // Page not found : creating.
        struct pagedir_elt newnode;
        newnode.fp = page_ptr;
        newnode.usecount = 1;
        newnode.size = size;
        newnode.exclusive = exclusive;
        newnode.acquired = acquired;
        (*page_dir)[page_ptr] = newnode;
    } else {
        ASSERT( acquired == ret->second.acquired);
        if ( ret->second.acquired &&
             !ret->second.exclusive &&
             exclusive ) {
            // TODO : fix this.
            dfrt_mem_blocking_upgrade(ret->second.fp, ret->second.size);
        }
        ret->second.exclusive |= exclusive;

        ret->second.usecount += 1;
    }
}


void  page_managed_acquire( void * page_ptr, size_t size, bool exclusive ) {
    PageMap::iterator ret = page_dir->find(page_ptr);
    if ( ret == page_dir->end() ) {
        // no such page.
        FATAL( "page_managed_acquire, but no page requested.");
    } else if ( ret->second.acquired ) {
        FATAL("page_managed_acquire, but page acquired already.");
    }
    ret->second.acquired = true;

}




bool  page_dec_count( void * page_ptr, size_t size, bool exclusive ) {
    PageMap::iterator ret  = page_dir->find( page_ptr );


    if ( ret == page_dir->end() ) {
        FATAL("Cannot decrement page : page not found.");
    } else if ( ! ret->second.acquired ) {
        FATAL("Cannot decrease the counter for wanted pages.");
    } else if ( ret->second.usecount > 1 ) {
        ret->second.usecount -= 1;
        return true;
    } else {
        // We need to free it.
        ret->second.usecount = 0;
        page_dir->erase(page_ptr);

        return true;
    }
}


int pages_acquired() {
    // TODO : fix it
    return page_dir->size();
}

int pages_wanted() {
    // TODO : fix it.
    return page_dir->size();
}


